///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file formic/fqmc/vmc_walker.h
///
/// \brief   header file for the variational monte carlo walker class
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef FORMIC_FQMC_VMC_WALKER_HEADER
#define FORMIC_FQMC_VMC_WALKER_HEADER

#include<vector>

#include<formic/random/random.h>
#include<formic/fqmc/walker.h>
#include<formic/wfn/configuration.h>
#include<formic/fqmc/accumulator.h>
#include<formic/wfn/coeff_obj.h>
#include<formic/fqmc/distance_proposer.h>

namespace formic {

  class InputBase;

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A class for a variational monte carlo walker
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  template<class S> class VMC_Walker : public formic::Walker {

    private:

      /// \brief  whether the most recently proposed configuration was accepted
      bool _accepted;

      /// \brief  a counter to track how long the walker has been on the current configuration
      double _count;

      /// \brief  number of accepted moves
      double _nacc;

      /// \brief  number or proposed moves
      double _npro;

      /// \brief  how many moves should be attempted between samples
      int _mps;

      /// \brief  the number of steps taken since the last accumulation
      int _steps;

      /// \brief  the number of irreducible representations
      int _nirr;

      /// \brief  the configuration the walker is on
      formic::Configuration _config;

      /// \brief  an object specifying the difference between the current and proposed configurations
      formic::ConfigDiff _config_diff;

      /// \brief  the direct product table for the symmetry point group
      std::vector<int> _dpt;

      /// \brief  the irreps of the orbitals
      std::vector<int> _orb_irrep;

      /// \brief  a random number generator
      formic::LaggedFibonacci<> _lfg;

      /// \brief  pointer to the coefficient object used as the guiding function
      boost::shared_ptr<formic::CoeffObj<S> > _gf;

      /// \brief  pointers to coefficient objects that should be updated as we move around
      std::vector<boost::shared_ptr<formic::CoeffObj<S> > > _co_vec;

      /// \brief  pointers to accumulators
      std::vector<boost::shared_ptr<formic::Accumulator<S> > > _accumulators;

      /// \brief  object to propose moves based on distance between orbitals
      formic::fqmc::DistProposer _dist_prop;

    private:

      double propose_config_diff(const formic::InputBase & userinp);

    public:

      /// \brief  returns a reference to the walker's configuration
      formic::Configuration & config() { return this->_config; }

      /// \brief  returns a constant reference to the walker's configuration
      const formic::Configuration & config() const { return this->_config; }

      /// \brief  returns a reference to the walker's random number generator
      formic::LaggedFibonacci<> & lfg() { return this->_lfg; }

      /// \brief  returns a constant reference to the walker's random number generator
      const formic::LaggedFibonacci<> & lfg() const { return this->_lfg; }

      /// \brief  returns a reference to the walker's guiding function pointer
      boost::shared_ptr<formic::CoeffObj<S> > & gf() { return this->_gf; }

      /// \brief  returns a constant reference to the walker's guiding function pointer
      const boost::shared_ptr<formic::CoeffObj<S> > & gf() const { return this->_gf; }

      /// \brief  returns a reference to the walker's vector of other (non-guiding) coefficient objects
      std::vector<boost::shared_ptr<formic::CoeffObj<S> > > & co_vec() { return this->_co_vec; }

      /// \brief  returns a constant reference to the walker's vector of other (non-guiding) coefficient objects
      const std::vector<boost::shared_ptr<formic::CoeffObj<S> > > & co_vec() const { return this->_co_vec; }

      /// \brief  returns a reference to the walker's vector of accumulators
      std::vector<boost::shared_ptr<formic::Accumulator<S> > > & accumulators() { return this->_accumulators; }

      /// \brief  returns a constant reference to the walker's vector of accumulators
      const std::vector<boost::shared_ptr<formic::Accumulator<S> > > & accumulators() const { return this->_accumulators; }

      VMC_Walker(const formic::InputBase & userinp);

      void propose_move(const formic::InputBase & userinp);

      void accumulate(const formic::InputBase & userinp);

      void move(const formic::InputBase & userinp);

      void final_accumulate(const formic::InputBase & userinp);

      void reset(const formic::InputBase & userinp, const bool reset_co = true);

      void exact_sample(const formic::InputBase & userinp);

  };

}

#endif
